const puppeteer = require('puppeteer');
const fs = require('fs');
const axios = require('axios');

/**
 * 初始化puppeteer浏览器页面并(可选)挂代理
 * @param {string} [proxy] - 代理地址，如 'http://127.0.0.1:666'
 * @returns {Promise<{browser, pages, page}>}
 */
async function initPage(proxy) {
    const args = [
        '--no-sandbox',
        '--disable-setuid-sandbox',
        '--window-size=1920,1080'
    ];
    if (proxy) args.push(`--proxy-server=${proxy}`);

    const browser = await puppeteer.launch({
        headless: true,
        args
    });

    const pages = await browser.pages();
    const page = pages[0];

    // 可省略，仅调试用
    // global.dbrowser = browser;
    // global.dpages = pages;
    // global.dpage = page;

    await page.setViewport({ width: 1920, height: 1080 });

    await page.goto(
        "https://ecipuia.xiamenair.com/api/v1/oauth2/authorize?uia_type=st&lang=tw-hk&client_id=uiaweb_client&redirect_uri=https%3A%2F%2Fwww.xiamenair.com%2Ftw-hk%2F",
        { waitUntil: 'networkidle2' }
    );

    return { browser, pages, page };
}

/**
 * 自动化登录并获取验证码base64
 * 返回统一结构: { code, msg, response }
 */
async function loginAndGetCaptcha(page, account, password) {
    try {
        // 自动填写表单
        await page.type('#main-content', account);
        await page.type('.password', password);

        await page.waitForSelector('img.code-img.J_Code', { timeout: 5000 });

        // 获取验证码图像base64
        const imgBase64 = await page.evaluate(() => {
            const img = document.querySelector('img.code-img.J_Code');
            if (!img) return null;
            const canvas = document.createElement('canvas');
            canvas.width = img.naturalWidth;
            canvas.height = img.naturalHeight;
            const ctx = canvas.getContext('2d');
            ctx.drawImage(img, 0, 0);
            return canvas.toDataURL();
        });

        if (!imgBase64) {
            return { code: 2, msg: '未能获取验证码图片', response: null };
        }

        const pos = imgBase64.indexOf(',');
        let imgBase64Str = (pos >= 0) ? imgBase64.slice(pos + 1) : imgBase64;
        let captchaCode = await getCaptchaCode(imgBase64Str);
        if (!captchaCode || !captchaCode.result) {
            return { code: 3, msg: '验证码识别失败', response: captchaCode };
        }
        await page.type('.code.code1.J_Captcha', captchaCode.result);
        console.log('验证码识别:', captchaCode);

        await delay(500);

        // 等待接口包监听
        const verify2Promise = safeWaitForApiPackage(page, {
            url: '/api/v1/oauth2/verify2',
            method: 'POST',
            timeout: 5000
        });
        const loginByStPromise = safeWaitForApiPackage(page, {
            url: '/user/channelLogin/loginByST',
            method: 'POST',
            timeout: 5000
        });

        await page.waitForSelector('#login-btn-1', { visible: true });
        await page.click('#login-btn-1');

        let pack = await verify2Promise;
        if (pack && pack.response && pack.response.body != null) {
            return { code: 0, msg: 'success', response: pack.response };
        } else {
            pack = await loginByStPromise;
            if (pack && pack.response) {
                return { code: 0, msg: 'success(loginByST)', response: pack.response };
            } else {
                return { code: 4, msg: '未能拦截到任何有效登录接口包', response: null };
            }
        }
    } catch (e) {
        console.error('loginAndGetCaptcha异常:', e);
        return { code: 9, msg: e.message || '未知异常', response: null };
    }
}

/**
 * 封装 waitForApiPackage 返回错误不抛出reject，而是返回 null
 */
async function safeWaitForApiPackage(page, opts) {
    try {
        return await waitForApiPackage(page, opts);
    } catch (e) {
        console.error('waitForApiPackage拦包异常:', e.message);
        return null;
    }
}

/**
 * 清除页面cookie及storage
 */
async function clearAllCookies(page) {
    const cookies = await page.cookies();
    if (cookies.length > 0) {
        await page.deleteCookie(...cookies);
    }
    await page.evaluate(() => {
        localStorage.clear();
        sessionStorage.clear();
    });
}

/**
 * 调用验证码服务
 */
async function getCaptchaCode(img_base64) {
    const config = {
        method: 'post',
        url: 'http://api.zhunjisofa.com:6688/api/ocr/image',
        headers: {
            'Accept-Language': 'zh-CN,zh;q=0.9',
            'Content-Type': 'application/json'
        },
        data: JSON.stringify({ img_base64 })
    };
    try {
        const response = await axios(config);
        return response.data;
    } catch (err) {
        return { code: -1, msg: '验证码接口异常', detail: err.message }
    }
}

/**
 * 等待指定毫秒数
 */
function delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

/**
 * 等待拦截指定url、method的XHR接口请求及其响应内容
 * 若超时则reject，外层一般要catch或用safeWaitForApiPackage包装
 */
function waitForApiPackage(page, { url, method = undefined, timeout = 10000 }) {
    function isMatch(u, m) {
        let urlOK = typeof url === 'string' ? u.includes(url) : url.test(u);
        let methodOK = method ? (m && m.toUpperCase() === method.toUpperCase()) : true;
        return urlOK && methodOK;
    }

    let lastRequest = null;
    async function getResponseBodySafe(response) {
        const reqMethod = response.request().method().toUpperCase();
        const status = response.status();
        if (reqMethod === 'OPTIONS' || [204, 304].includes(status)) return null;
        try {
            return await response.json();
        } catch {
            try {
                return await response.text();
            } catch {
                return null;
            }
        }
    }

    return new Promise((resolve, reject) => {
        let timer = setTimeout(() => {
            cleanup();
            reject(new Error('waitForApiPackage: 超时未能拦截到目标接口包'));
        }, timeout);

        async function onRequest(request) {
            if (isMatch(request.url(), request.method())) {
                lastRequest = {
                    url: request.url(),
                    method: request.method(),
                    headers: request.headers(),
                    postData: request.postData()
                };
            }
        }

        async function onResponse(response) {
            if (isMatch(response.url(), response.request().method())) {
                const body = await getResponseBodySafe(response);
                cleanup();
                resolve({
                    request: lastRequest,
                    response: {
                        url: response.url(),
                        status: response.status(),
                        headers: response.headers(),
                        body
                    }
                });
            }
        }

        function cleanup() {
            clearTimeout(timer);
            page.off('request', onRequest);
            page.off('response', onResponse);
        }

        page.on('request', onRequest);
        page.on('response', onResponse);
    });
}

module.exports = {
    initPage,
    loginAndGetCaptcha,
    clearAllCookies,
    delay
};
